#include "messagebuilder.h"
#include <ctime>
#include <stdarg.h>
#include "logger.h"
#include "soc/soc_misc.h"
#include "os/thread.h"
#include "lib/stringex.h"


#define CHECK_LOG_ON() \
    if(!m_logger)   \
        return *this;\
    if(!m_forceWrite && !m_logger->IsLogOn(m_level))\
        return *this

#define PRINT_TO_MSG(newline) do{\
    CHECK_LOG_ON();\
    va_list args;\
    va_start(args, fmt);\
    auto msg = StringEx::Format(fmt, args); \
    va_end(args);\
    \
    m_autoNewLine = newline;\
    m_messages.append(msg);\
}while(0)

#define AppendFloatDouble(msg) do{\
    if(m_precision < 0)\
    {\
        m_messages += MiniFormat("%f", msg);\
    }\
    else\
    {\
        std::string fmt = "%." + std::to_string(m_precision) + "f";\
        m_messages += MiniFormat(fmt.c_str(), msg);\
    }\
}while(0)


MessageBuilder::MessageBuilder(Logger *logger)
{
    m_logger = logger;
    //set default
    if(m_logger)
    {
        m_autoSpacing = logger->Configuration.AutoSpacing;
        m_autoNewLine = logger->Configuration.AutoNewLine;
    }
}

MessageBuilder::~MessageBuilder()
{
    if(m_logger == nullptr)
        return;
    if(!m_forceWrite && !m_logger->IsLogOn(m_level))
        return;

    if(m_formatEnable)
    {
        std::string format = GetFormatMessage();
        if(!format.empty())
            m_messages.insert(0, format.c_str(), format.size());
    }
    
    if(m_autoNewLine)
        m_messages.append(LOGGER_NEWLINE, sizeof(LOGGER_NEWLINE) - 1);
        
    if(!m_messages.empty())
        m_logger->WriteToStream(m_messages.c_str(), m_messages.size(), true);
}

MessageBuilder &MessageBuilder::operator<<(bool msg)
{
    CHECK_LOG_ON();
    if(m_logger->Configuration.BooleanShowConvert)
    {
        m_messages += msg? "true" : "false";
    }
    else
    {
        m_messages += msg? "1" : "0";
    }

    AppendSpace();
    return *this;
}

MessageBuilder &MessageBuilder::operator<<(uint8_t msg)
{    
    CHECK_LOG_ON();    
    m_messages += std::to_string(msg);
    AppendSpace();
    return *this;
}

MessageBuilder &MessageBuilder::operator<<(int8_t msg)
{
    CHECK_LOG_ON();  
    m_messages += std::to_string(msg);
    AppendSpace();
    return *this;
}

MessageBuilder &MessageBuilder::operator<<(uint16_t msg)
{    
    CHECK_LOG_ON();  
    m_messages += std::to_string(msg);
    AppendSpace();
    return *this;
}

MessageBuilder &MessageBuilder::operator<<(int16_t msg)
{
    CHECK_LOG_ON();  
    m_messages += std::to_string(msg);
    AppendSpace();
    return *this;
}

MessageBuilder &MessageBuilder::operator<<(uint32_t msg)
{
    CHECK_LOG_ON();  
    m_messages += std::to_string(msg);
    AppendSpace();
    return *this;
}

MessageBuilder &MessageBuilder::operator<<(int32_t msg)
{
    CHECK_LOG_ON();  
    m_messages += std::to_string(msg);
    AppendSpace();
    return *this;
}

MessageBuilder &MessageBuilder::operator<<(uint64_t msg)
{
    CHECK_LOG_ON();  
    m_messages += std::to_string(msg);
    AppendSpace();
    return *this;
}

MessageBuilder &MessageBuilder::operator<<(int64_t msg)
{
    CHECK_LOG_ON();  
    m_messages += std::to_string(msg);
    AppendSpace();
    return *this;
}

MessageBuilder &MessageBuilder::operator<<(float msg)
{    
    CHECK_LOG_ON();  
    AppendFloatDouble(msg);
    AppendSpace();
    return *this;
}

MessageBuilder &MessageBuilder::operator<<(double msg)
{
    CHECK_LOG_ON();  
    AppendFloatDouble(msg);
    AppendSpace();
    return *this;
}

MessageBuilder &MessageBuilder::operator<<(char msg)
{
    CHECK_LOG_ON();
    m_messages += msg;
    AppendSpace();
    return *this;
}

MessageBuilder &MessageBuilder::operator<<(const char *msg)
{   
    CHECK_LOG_ON();
    m_messages.append(msg);
    AppendSpace();
    return *this;
}

MessageBuilder &MessageBuilder::operator<<(void *msg)
{
    CHECK_LOG_ON();
    m_messages += MiniFormat("%p", msg);
    AppendSpace();
    return *this;
}

MessageBuilder &MessageBuilder::operator<<(const std::string &msg)
{
    CHECK_LOG_ON();  
    m_messages.append(msg);
    AppendSpace();
    return *this;
}

MessageBuilder &MessageBuilder::operator<<(const std::string &&msg)
{
    CHECK_LOG_ON();  
    m_messages.append(msg);
    AppendSpace();
    return *this;
}

MessageBuilder &MessageBuilder::Print(const char *fmt, ...)
{
    PRINT_TO_MSG(false);
    return *this;  
}

MessageBuilder &MessageBuilder::PrintLn(const char *fmt, ...)
{
    PRINT_TO_MSG(true);
    return *this;  
}

MessageBuilder &MessageBuilder::NoSpace()
{
    m_autoSpacing = false;
    return *this;
}

MessageBuilder &MessageBuilder::Space()
{
    m_autoSpacing = true;
    return *this;   
}

MessageBuilder &MessageBuilder::NoNewline()
{
    m_autoNewLine = false;
    return *this;   
}

MessageBuilder &MessageBuilder::Newline()
{
    m_autoNewLine = true;
    return *this;   
}

MessageBuilder &MessageBuilder::NoFormat()
{
    m_formatEnable = false;
    return *this;   
}

MessageBuilder &MessageBuilder::Format()
{
    m_formatEnable = true;
    return *this;   
}

MessageBuilder &MessageBuilder::AsRaw()
{
    return NoSpace().NoNewline().NoFormat();
}

MessageBuilder &MessageBuilder::Force()
{
    m_forceWrite = true;
    return *this;
}

MessageBuilder &MessageBuilder::AsTag(const std::string &tag)
{
    m_tag = tag;
    return *this;
}

MessageBuilder &MessageBuilder::ConfigLevel(LoggerLevel level)
{
    m_level = level;
    return *this;
}

MessageBuilder &MessageBuilder::Precision(int precision)
{
    m_precision = precision;
    return *this;
}

void MessageBuilder::AppendSpace()
{
    if(m_autoSpacing)
    {
        m_messages.append(" ");
    }
}

//format固定: loggerName tag level [date time] [threadName-threadId] [fileName:fileLine func] msg
std::string MessageBuilder::GetFormatMessage()
{
    if(m_logger == nullptr)
        return "";
    if(!m_formatEnable)
        return "";
    
    std::string format;
    if(m_logger->Configuration.ShowLoggerName)
    {
        format.append(m_logger->m_name);
        format.append(" ");
    }
    
    if(m_logger->Configuration.ShowTag)
    {
        format.append(m_tag);
        format.append(" ");
    }

    if(m_logger->Configuration.ShowLevel)
    {
        format.append(ConvertLevelToShortString(m_level));
        format.append(" ");
    }

    if(m_logger->Configuration.ShowDate || m_logger->Configuration.ShowTime)
    {
        format.append("[");
        if(m_logger->Configuration.ShowDate)
        {
            
        }
        if(m_logger->Configuration.ShowTime)
        {
            format.append(GetTimeString());
        }
        format.append("]");
    }

    if(m_logger->Configuration.ShowThreadName || 
       m_logger->Configuration.ShowThreadId)
    {
        format.append("[");
        if(m_logger->Configuration.ShowThreadName)
        {
            format.append(Thread::GetThreadName(Thread::CurrentThreadId()));
            format.append("-");
        }
        if(m_logger->Configuration.ShowThreadId)
        {
            format.append(MiniFormat("%d", Thread::CurrentThreadId()));
        }
        format.append("]");
    }

    if(m_logger->Configuration.ShowFileName || 
       m_logger->Configuration.ShowFileLine || 
       m_logger->Configuration.ShowFuncName)
    {
        format.append("[");
        if(m_logger->Configuration.ShowFileName)
        {
            if(m_logger->Configuration.ShortFileNameEn)
                ConvertToShortFileName(m_fileName);
            format.append(m_fileName);
            format.append(":");
        }
        if(m_logger->Configuration.ShowFileLine)
        {        
            format.append(MiniFormat("%d", m_fileLine));
            format.append(" ");
        }  
        if(m_logger->Configuration.ShowFuncName)
        {
            format.append(m_funcName);
        }
        format.append("]");
    }

    if(format.back() != ' ')
        format.append(" ");
    return format;

}

char* MessageBuilder::GetTimeString()
{
#define MAX_LEN 13

    static char buf[MAX_LEN];
    uint8_t len = 0;
    uint32_t tick = SOC_Misc::CurrentCpuMilliseconds();
    time_t s = tick / 1000;
    uint32_t ms = tick % 1000;
    struct tm *tm;

    tm = localtime((const time_t*)&s);
    len = snprintf(buf, MAX_LEN, 
                 "%02d:%02d:%02d:%03d", 
                  tm->tm_hour, tm->tm_min, tm->tm_sec, ms);
		//
    buf[len] = '\0';
    return buf;

#undef MAX_LEN
}

std::string MessageBuilder::ConvertLevelToShortString(LoggerLevel level)
{
    switch(level)
    {
    case LoggerLevel::Debug:
        return "D";
    case LoggerLevel::Info:
        return "I";
    case LoggerLevel::Warn:
        return "W";
    case LoggerLevel::Error:
        return "E";
    case LoggerLevel::Fatal:
        return "F";
    default:
        return "U";
    }

    return "U";
}

void MessageBuilder::ConvertToShortFileName(std::string &fileName)
{
    if(fileName.empty() || fileName.length() < 2) return;

    int index = -1;
    for(int i = fileName.size() - 1; i >= 0; i--)
    {
        if(fileName.at(i) == '\\' || fileName.at(i) == '/')
        {
            index = i;
            break;
        }
    }

    if(index >= 0)
    {
        fileName = fileName.substr(index+1);
    }
}

std::string MessageBuilder::MiniFormat(char const *const format, ...)
{
#define FORMAT_BUF_MAX 30

    std::string res;
    uint8_t len;
    va_list argList;
    char buf[FORMAT_BUF_MAX];

    va_start(argList, format);
    len = vsnprintf(buf, FORMAT_BUF_MAX, format, argList);
    va_end(argList);
        
    return std::string(buf);

#undef FORMAT_BUF_MAX
}